由前篇最後做出來的效果,在我看來並不是那麼好
原因如下:
基於以上兩點,所以我在使用之前的方法,先構想我希望可以如何撰寫
CreateCrawler().GetAsync(ConfigureRequest);
void ConfigureReqeust(HttpRequestMessage req)
{
SetUrl(req);
SetMethod(req);
return req;
}
void SetUrl(HttpRequestMessage req,string url)
{
if(url == null) throw new Exception($"{nameof(url)} is null");
req.RequestUri = new Uri(url);
}
void SetMethod(HttpRequestMessage req,string method)
{
method = method ?? HttpMethod.Get;
req.SetMethod(method);
}
這些只是零稀的片段,不過大約可以做為上述需求的參考
也就是說
你PM喔!需求那麼多還那麼討厭
不過因為這些項目通通都不屬於Crawler,已經是HttpRequestMessage的事情了
所以,這些內容也通通都不應該出現在Crawler 裡面
而對於HttpRequestMessage的設定,在未來其他地方也很有可能會使用
所以不如好好的設計一番吧
public class HttpRequestMessageBuilder
{
private IList<Action<HttpRequestMessage>> _configs;
public HttpRequestMessageBuilder()
{
_configs = new List<Action<HttpRequestMessage>>();
}
public void Set(Action<HttpRequestMessage> config)
=> _configs.Add(config);
public HttpRequestMessage Build(Func<HttpRequestMessage> factory = default)
=> _configs.Aggregate(
factory() ?? new HttpRequestMessage(),
(req,config) => config(req));
}
先做個最簡單的設定法
這是透過Set方法把作為設定Request 的方法都先放在集合中
當開始Build的時候,再依序套用在參數帶入的Request Factory 所以建立的 Request
最後回傳設定好的 Request
至少這已經不太可能會miss掉任何 Request 可以定義的內容
而 Request 可以定義的屬性非常多,實際上我們在開發不會每一個都用到
所以一般就已常用,和當下會用到的來做處理即可
public void SetUrl(Uri url)
=> Set(req => req.RequestUri = url);
public void SetMethod(HttpMethod method)
=> Set(req => req.Method = method);
public void SetContent(HttpContent content)
=> Set(req => req.Content = content);
public void ConfigureHeader(Action<HttpRequestHeaders> config)
=> Set(req => config(req.Headers));
這裡用Set 而不是直接_configs.Add 的原因很簡單
除了少幾個字以外,如果我想對於設定做全面的調整時
此時就多了一個 Set 可以用而不需要複寫或擴充 IList<>
這樣就可以符合不使用參數而是使用方法描述來做設定的作法
此時因為常見的幾個方法被分離出來了,所以加上一些驗證和預設值吧
public void SetUrl(Uri url)
=> Set(req => req.RequestUri = (url ?? throw new ArgumentNullException(nameof(url))));
public void SetMethod(HttpMethod method)
=> Set(req => req.Method = method ?? HttpMethod.Get);
public void SetContent(HttpContent content)
=> Set(req => req.Content = content ?? new StringContent(string.Empty));
public void ConfigureHeader(Action<HttpRequestHeaders> config)
=> Set(req => config?.Invoke(req.Headers));
這樣不但每一個屬性分離帶入
且驗證也不會交錯複雜,而且也沒有多餘的方法
最後轉型的多載當然是不能影響這些事情
所以接下來就再用擴充漂亮的展開吧